package run.jingjing.websocket.timer;

import io.netty.util.HashedWheelTimer;
import io.netty.util.Timeout;
import io.netty.util.TimerTask;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.RedisTemplate;
import run.jingjing.websocket.model.LWsGroup;
import run.jingjing.websocket.model.LWsTopic;
import run.jingjing.websocket.template.WebSocketTemplate;
import run.jingjing.websocket.model.RunningTopic;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;

/**
 * @author: jingjing
 * @date: 2021/6/24
 * @description:
 */
@Slf4j
public class LocalOnlineCountTask implements TimerTask {

    private RunningTopic runningTopic;

    private WebSocketTemplate webSocketTemplate;

    private RedisTemplate redisTemplate;

    private HashedWheelTimer timer;

    private Integer intervalSecond = 5;

    private Integer maxIdleTime = 10;

    private Integer curIdleTime = 0;

    private AtomicBoolean prepareToClose = new AtomicBoolean(false);

    public LocalOnlineCountTask(RunningTopic runningTopic, WebSocketTemplate webSocketTemplate, RedisTemplate redisTemplate, HashedWheelTimer timer, Integer intervalSecond) {
        this.runningTopic = runningTopic;
        this.webSocketTemplate = webSocketTemplate;
        this.redisTemplate = redisTemplate;
        this.timer = timer;
        this.intervalSecond = intervalSecond;
        if (runningTopic == null || webSocketTemplate == null || redisTemplate == null || timer == null) {
            log.error("constructor param of LocalOnlineCountTask is null");
            throw new RuntimeException();
        }
    }

    @Override
    public void run(Timeout timeout) {
        LWsGroup group = runningTopic.getlWsGroup();
        LWsTopic topic = runningTopic.getlWsTopic();
        if (!topic.isEmpty()) {
            curIdleTime = 0;
            String key = webSocketTemplate.getLocalOnlineCountKey(group.getName(), topic.getName());
            // count topic online
            redisTemplate.opsForValue().set(key, topic.size(), intervalSecond * 3, TimeUnit.SECONDS);
        } else if (curIdleTime >= maxIdleTime) {
            if (prepareToClose.compareAndSet(false, true)) {
                timer.newTimeout(new TimerTask() {
                    @Override
                    public void run(Timeout timeout) throws Exception {
                        runningTopic.stop();
                    }
                }, 1L, TimeUnit.SECONDS);
                log.info("group:{},topic:{} is fallow, its runningTopic would be closed one second later", group.getName(), topic.getName());
            }
        } else {
            curIdleTime++;
        }
        Timeout newTimeout = timer.newTimeout(this, intervalSecond, TimeUnit.SECONDS);
        runningTopic.removeTimeout(timeout);
        runningTopic.addTimeouts(newTimeout);
    }
}
